package cn.assumejson.springframework.context.context.annotation;

import cn.assumejson.springframework.beans.factory.BeansException;
import cn.assumejson.springframework.beans.factory.config.BeanDefinition;
import cn.assumejson.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import cn.assumejson.springframework.beans.factory.config.SingletonBeanRegistry;
import cn.assumejson.springframework.beans.factory.support.BeanDefinitionRegistry;
import cn.assumejson.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import cn.assumejson.springframework.beans.factory.support.RootBeanDefinition;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;

import java.beans.Introspector;
import java.lang.reflect.Method;
import java.util.*;

import static cn.assumejson.springframework.context.context.annotation.AnnotationConfigUtils.ASPECTJ_AUTO_PROXY_REGISTRAR_BEAN_NAME;

/**
 * 配置类解析的后置处理器
 *
 * @author Mr. Li
 * @version 1.0
 * @since 1.0 2021/12/5 14:09
 */
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor {

    private Set<BeanDefinition> registerBeanDefinitions = new HashSet<>();

    private Map<String, Class<?>> beanClassMap = new HashMap<>();

    private BeanDefinitionRegistry registry;

    /**
     * 提前注册注解的配置
     *
     * @param registry the bean definition registry used by the application context
     * @throws BeansException
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        Assert.notNull(registry, "registry is must  not be null");
        this.registry = registry;

        processConfigBeanDefinitions(registry);
    }

    private void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
        if (registry instanceof SingletonBeanRegistry) {
            // 获取所有的配置类
            String[] candidateNames = registry.getBeanDefinitionNames();
            Set<BeanDefinition> configurationBeanDefinition = new HashSet<>();
            for (String beanName : candidateNames) {
                BeanDefinition beanDefinition = registry.getBeanDefinition(beanName);
                Class<?> beanClass = beanDefinition.getBeanClass();
                Configuration annotation = beanClass.getAnnotation(Configuration.class);
                if (ObjectUtil.isNotNull(annotation)) {
                    configurationBeanDefinition.add(beanDefinition);
                }
            }

            // Parsing configuration
            for (BeanDefinition beanDefinition : configurationBeanDefinition) {
                // 解析每一个配置类中是否包含了各种enable注解，@Bean 注解
                // 解析@Import类
                parsingImport(beanDefinition, registry);
                parsingBean(beanDefinition, registry);
                // 解析 路径扫描
                parsingAllComponent(beanDefinition, registry);
            }
            // @Bean生成BeanDefinition并注册
            beanClassMap.forEach(this::loadBeanDefinitionsForBeanMethod);

            // 处理@ImportResource("spring.xml")
//            loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());

            // 处理ImportBeanDefinitionRegistrar，调用registerBeanDefinitions()方法
            loadBeanDefinitionsFromRegistrars(this.registerBeanDefinitions);
        }
    }

    /**
     * 回调注册
     *
     * @param registerBeanDefinitions All ImportBeanDefinitionRegistrar Implementation
     */
    private void loadBeanDefinitionsFromRegistrars(Set<BeanDefinition> registerBeanDefinitions) {
        for (BeanDefinition registerBeanDefinition : registerBeanDefinitions) {
            ((ImportBeanDefinitionRegistrar) registerBeanDefinition.getBean()).registerBeanDefinitions(this.registry);
        }
    }

    /**
     * 将所有的@bean 编译成 beanDefinition
     *
     * @param beanName  beanName
     * @param beanClass beanClass
     */
    private void loadBeanDefinitionsForBeanMethod(String beanName, Class<?> beanClass) {
        this.registry.registerBeanDefinition(beanName, new RootBeanDefinition(beanClass));
    }

    private void parsingAllComponent(BeanDefinition beanDefinition, BeanDefinitionRegistry registry) {
        Class<?> beanClass = beanDefinition.getBeanClass();
        ComponentScan annotation = beanClass.getAnnotation(ComponentScan.class);

        String[] baseScan = new String[]{};

        if (ArrayUtil.isNotEmpty(baseScan)) {
            baseScan = annotation.basePackages();
        } else {
            baseScan = new String[]{beanClass.getPackage().getName()};
        }
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry);
        scanner.scan(baseScan);
    }

    /**
     * 解析 @Bean 注解
     *
     * @param beanDefinition beanDefinition
     * @param registry       registry
     */
    private void parsingBean(BeanDefinition beanDefinition, BeanDefinitionRegistry registry) {
        // 解析所有的方法是否使用了@Bean 注解
        Class<?> beanClass = beanDefinition.getBeanClass();
        for (Method declaredMethod : beanClass.getDeclaredMethods()) {
            Bean annotation = declaredMethod.getAnnotation(Bean.class);
            if (null != annotation) {
                // TODO 这里需要处理类型
                String beanName = "";
                String[] name = annotation.name();
                if (ArrayUtil.isNotEmpty(name)) {
                    beanName = name[0];
                } else {
                    String[] value = annotation.value();
                    if (ArrayUtil.isNotEmpty(value)) {
                        beanName = value[0];
                    }
                }
                if (StrUtil.isBlank(beanName)) {
                    beanName = Introspector.decapitalize(declaredMethod.getName());
                }
                Class<?> returnType = declaredMethod.getReturnType();
                // 注册BeanDefinition
                this.beanClassMap.put(beanName, returnType);
            }
        }
    }

    /**
     * 解析 Import
     *
     * @param beanDefinition beanDefinition
     * @param registry       registry
     */
    private void parsingImport(BeanDefinition beanDefinition, BeanDefinitionRegistry registry) {
        Class<?> beanClass = beanDefinition.getBeanClass();
        Import annotation = beanClass.getAnnotation(Import.class);
        if (null != annotation) {
            // 获取 导入的bean
            for (Class<?> clazz : annotation.value()) {
                RootBeanDefinition rbd = new RootBeanDefinition(clazz);
                registry.registerBeanDefinition(Introspector.decapitalize(clazz.getSimpleName()), rbd);
            }
        }
        EnableAspectJAutoProxy enableAspectJAutoProxy = beanClass.getAnnotation(EnableAspectJAutoProxy.class);
        if (null != enableAspectJAutoProxy) {
            Class<?>[] value = EnableAspectJAutoProxy.class.getAnnotation(Import.class).value();
            for (Class<?> aClass : value) {
                // 注册 BeanDefinitionRegister
                if (ImportBeanDefinitionRegistrar.class.isAssignableFrom(aClass)) {
                    registry.registerBeanDefinition(ASPECTJ_AUTO_PROXY_REGISTRAR_BEAN_NAME, new RootBeanDefinition(aClass));
                    // 添加到 registerBeanDefinition中
                    this.registerBeanDefinitions.add(registry.getBeanDefinition(ASPECTJ_AUTO_PROXY_REGISTRAR_BEAN_NAME));
                }
            }
        }
    }

    /**
     * 使用 cglib 代理对象处理@Bean 注解
     *
     * @param beanFactory DefaultListableBeanFactory
     * @throws BeansException
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}
